/*
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
* Description: demuser for mp4
* Author: HiMobileCam middleware develop team
* Create: 2017-12-22
*/

#include "mp4_format.h"

#ifndef __HuaweiLite__
#include <sys/syscall.h>
#endif
#include "securec.h"
#include "hi_demuxer_err.h"
#include "ffdemux_log.h"

#include "ffaacparser.h"
#include "ffavcparser.h"
#include "ffhevcparser.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* __cplusplus */

#define FFMPEG_AAC_ADTS_LEN     7
#define MAX_TRY_READ_TIMES      20

#define FFMPEG_H264_NAL_MASK  0x1f
#define FFMPEG_H265_NAL_MASK  0x7f

typedef enum {
    AAC_PROFILE_MAIN = 0,
    AAC_PROFILE_LC,
    AAC_PROFILE_SSR,
    AAC_PROFILE_RESERVED,
} HiFfmpegAacProfile;

typedef enum {
    FFMPEG_AVC_SEI = 0x06,
    FFMPEG_AVC_IDR = 0x05,
    FFMPEG_AVC_SPS = 0x07,
    FFMPEG_AVC_PPS = 0x08,
} HiFfmpegAvcNal;

typedef enum {
    FFMPEG_HEVC_PSLICE = 0x01,
    FFMPEG_HEVC_IDR = 0x13,
    FFMPEG_HEVC_SPS = 0x21,
    FFMPEG_HEVC_PPS = 0x22,
    FFMPEG_HEVC_VPS = 0x20,
    FFMPEG_HEVC_SEI = 0x27
} HiFfmpegHevcNal;

static const HI_U32 g_sampleRateTag[AAC_SAMPLE_RATE_NUM] = {
    96000, 88200, 64000, 48000, 44100, 32000,
    24000, 22050, 16000, 12000, 11025, 8000, 7350
};

typedef struct {
    HI_S32 sampleRateidx;
    HI_U32 frameLength;
    HI_U32 profile;
    HI_S32 chConfig;
} FfAdtsHeaderPartInfo;

/*
 * sampleRateidx !< aacPlus sampling frequency (incl. SBR)
 * frameLength !< raw data length
 * profile : 0 = main, 1 = LC, 2 = SSR, 3 = reserved
 */
static HI_VOID Mp4GetAdtsHeader(FfAdtsHeaderPartInfo info, HI_U8 adtsHeadBuf[], HI_U32 bufLen)
{
    if (bufLen < FFMPEG_AAC_ADTS_LEN) {
        return;
    }

    AacAdtsHeader adtsHeader = { 0 };
    HI_U32 frameLen = info.frameLength + FFMPEG_AAC_ADTS_LEN;

    /* fixed fields - should not change from frame to frame */
    adtsHeader.sync = 0x0fff;              /* 12bit: */
    adtsHeader.id = 0;                      /* 1bit: MPEG bit - should be 1 */
    adtsHeader.layer = 0;                   /* 2bit: MPEG layer - should be 0 */
    adtsHeader.protectBit = 1;              /* 1bit: */
    adtsHeader.pofile = info.profile;       /* 2bit: */
    adtsHeader.sampRateIdx = info.sampleRateidx; /* 4bit: */
    adtsHeader.privateBit = 0;              /* 1bit: */
    adtsHeader.channelConfig = info.chConfig;/* 3bit: */
    adtsHeader.origCopy = 0;                /* 1bit: */
    adtsHeader.home = 0;                    /* 1bit: */

    /* variable fields - can change from frame to frame */
    adtsHeader.copyBit = 0;                   /* 1bit: */
    adtsHeader.copyStart = 0;                 /* 1bit: */
    adtsHeader.frameLength = frameLen; /* 13bit: */
    adtsHeader.bufferFull = 0x07ff;          /* 11bit: */
    adtsHeader.numRawDataBlocks = 0;          /* 2bit: */

    HI_U32 head0 = adtsHeader.sync << (32 - 12);
    head0 |= adtsHeader.id << (32 - 12 - 1);
    head0 |= adtsHeader.layer << (32 - 12 - 1 - 2);
    head0 |= adtsHeader.protectBit << (32 - 12 - 1 - 2 - 1);

    head0 |= adtsHeader.pofile << (32 - 12 - 1 - 2 - 1 - 2);
    head0 |= adtsHeader.sampRateIdx << (32 - 12 - 1 - 2 - 1 - 2 - 4);
    head0 |= adtsHeader.privateBit << (32 - 12 - 1 - 2 - 1 - 2 - 4 - 1);
    head0 |= adtsHeader.channelConfig << (32 - 12 - 1 - 2 - 1 - 2 - 4 - 1 - 3);
    head0 |= adtsHeader.origCopy << (32 - 12 - 1 - 2 - 1 - 2 - 4 - 1 - 3 - 1);
    head0 |= adtsHeader.home << (32 - 12 - 1 - 2 - 1 - 2 - 4 - 1 - 3 - 1 - 1);
    head0 |= adtsHeader.copyBit << (32 - 12 - 1 - 2 - 1 - 2 - 4 - 1 - 3 - 1 - 1 - 1);
    head0 |= adtsHeader.copyStart << (32 - 12 - 1 - 2 - 1 - 2 - 4 - 1 - 3 - 1 - 1 - 1 - 1);
    head0 |= ((adtsHeader.frameLength >> (13 - 2)) & 0x3);

    HI_U32 head1 = adtsHeader.frameLength << (32 - 13 + 2);
    head1 |= adtsHeader.bufferFull << (32 - 13 + 2 - 11);
    head1 |= adtsHeader.numRawDataBlocks << (32 - 13 + 2 - 11 - 2);

    adtsHeadBuf[0] = (unsigned char)(head0 >> 24) & 0xff;
    adtsHeadBuf[1] = (unsigned char)(head0 >> 16) & 0xff;
    adtsHeadBuf[2] = (unsigned char)(head0 >> 8) & 0xff;
    adtsHeadBuf[3] = (unsigned char)(head0 >> 0) & 0xff;

    adtsHeadBuf[4] = (unsigned char)(head1 >> 24) & 0xff;
    adtsHeadBuf[5] = (unsigned char)(head1 >> 16) & 0xff;
    adtsHeadBuf[6] = (unsigned char)(head1 >> 8) & 0xff;

    return;
}

static HI_BOOL Mp4HasAdtsHeader(const HI_U8 *src, HI_U32 srcSize, const HI_U8 *dst, HI_U32 dstSize)
{
    if ((src == HI_NULL) || (dst == HI_NULL) || (srcSize < FFMPEG_AAC_ADTS_LEN) || (dstSize < FFMPEG_AAC_ADTS_LEN)) {
        return HI_FALSE;
    }

    /*
     * adts fixed header
     * syncword: 12bits, fixed,      0xFFF
     * ID:       1bit,   non-fixed,  MPEG version, 0 for MPEG-4, 1 for MPEG-2.
     * ...
     * Compare 28bits is enough, and we must compare without 'ID', because it is non-fixed
     */
    if ((src[0] != dst[0]) || (src[1] & 0xF7) != (dst[1] & 0xF7) ||
        (src[2] != dst[2]) || (src[3] & 0xF0) != (dst[3] & 0xF0)) {
        return HI_FALSE;
    }

    return HI_TRUE;
}

static HI_S32 Mp4AvbsfFilter(AVBSFContext *bsfCtx, AVPacket *srcPkt, AVPacket *dstPkt)
{
    HI_S32 ret = av_bsf_send_packet(bsfCtx, srcPkt);
    if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "av_bsf_send_packet failed ret %d\n", ret);
        return HI_FAILURE;
    }

    ret = av_bsf_receive_packet(bsfCtx, dstPkt);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                       "need more data input or no data output  ret: %d\n", ret);
        return HI_RET_FILE_EOF;
    } else if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO,
                       "av_bsf_receive_packet err  ret %x\n", ret);
        return HI_FAILURE;
    }

    AVPacket avpkt = { 0 };
    av_init_packet(&avpkt);
    /* drain all the remaining packets we cannot return */
    while (ret >= 0) {
        ret = av_bsf_receive_packet(bsfCtx, &avpkt);
        if (ret >= 0) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                "av_bsf_receive_packet extra packet len: %d are not handled\n", avpkt.size);
        }
        av_packet_unref(&avpkt);
    }

    // here we only consider about h264,h265, so extradata update do not
    // handle here
    return HI_SUCCESS;
}

static HI_S32 Mp4FilterPacket(FfDemuxFormatMember *formatMember)
{
    AVPacket filterPacket = { 0 };

    av_init_packet(&filterPacket);

    HI_S32 ret = Mp4AvbsfFilter(formatMember->h26XBsfCtx, &formatMember->pkt, &filterPacket);
    if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "Mp4AvbsfFilter err ret: %x\n", ret);
        av_packet_unref(&formatMember->pkt);
        /*
         * filter err may come from that repaired file stream-end have invalid frames,
         * so here just return EOF, ignore all frames at stream-end
         */
        return ret;
    }

    if (filterPacket.buf == HI_NULL) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO,
                       "av_bitstream_filter_filter, receive packet buffer is HI_NULL \n");
        av_packet_unref(&formatMember->pkt);
        return HI_FAILURE;
    }
    av_packet_move_ref(&formatMember->pkt, &filterPacket);

    return HI_SUCCESS;
}

// T-REC-H.264-201602-S!!PDF-E.pdf  B.1.1 Byte stream NAL unit syntax
static HI_U32 Mp4GetOneNalUnit(HI_U8 *outNaluType, const HI_U8 *buf, HI_U32 size)
{
    HI_U32 code;
    HI_U32 tmp = 0;
    HI_U32 pos = 0;

    if (size < 5) {
        return 0;
    }
    for (code = 0xffffffff, pos = 0; pos < 4; pos++) {
        tmp = buf[pos];
        code = (code << 8) | tmp;
    }

    if (code != 0x00000001) {
        return 0;
    }

    *outNaluType = buf[pos++] & 0xFF;

    for (code = 0xffffffff; pos < size; pos++) {
        tmp = buf[pos];

        if ((code = (code << 8) | tmp) == 0x00000001) {
            break;  // next start code is found
        }
    }

    if (pos == size) {  // next start code is not found, this must be the last nalu
        return size;
    } else {
        return pos - 4 + 1;
    }
}

static HI_S32 Mp4GetMetaResolution(FfDemuxFormatMember *formatMember, const HI_U8 *buf, HI_U32 size)
{
    HI_U32 width = 0;
    HI_U32 height = 0;

    if (AV_CODEC_ID_H264 == formatMember->metaStream->codecpar->codec_id) {
        AvcSpsInfo spsInfo = { 0 };
        FFAVCParserParseSps(&spsInfo, buf, size);
        if (spsInfo.picWidthInMbsMinus1 == 0) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                "Mp4GetMetaResolution spsInfo.u32PicWidthInMbsMinus1v:%d error \n", spsInfo.picWidthInMbsMinus1);
            return HI_FAILURE;
        }

        width = ((spsInfo.picWidthInMbsMinus1 + 1) * 16) - spsInfo.frameCropLeftOffset * 2 -
                   spsInfo.frameCropRightOffset * 2;
        height = ((2 - (spsInfo.frameMbsOnlyFlag ? 1 : 0)) * (spsInfo.picHeightInMapUnitsMinus1 + 1) * 16) -
                    (spsInfo.frameCropTopOffset * 2) - (spsInfo.frameCropBottomOffset * 2);
    } else if (formatMember->metaStream->codecpar->codec_id == AV_CODEC_ID_HEVC) {
        HEVCSpsInfo spsInfo = { 0 };
        FFHEVCParserParseSps(&spsInfo, buf, size);
        if (spsInfo.picWidthInLumaSamples == 0 || spsInfo.picHeightInLumaSamples == 0) {
            return HI_FAILURE;
        }
        width = spsInfo.picWidthInLumaSamples;
        height = spsInfo.picHeightInLumaSamples;
    }
    FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO,
        "Mp4GetMetaResolution width %d height %d \n", width, height);

    formatMember->metaStream->codecpar->width = width;
    formatMember->metaStream->codecpar->height = height;
    return HI_SUCCESS;
}

static HI_S32 Mp4GetFrame(FfDemuxFormatMember *formatMember)
{
    /* read frames from the file */
    HI_S32 ret = av_read_frame(formatMember->formatContext, &formatMember->pkt);
    if (ret == AVERROR_EOF) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "read endof ret: %d \n", ret);
        return HI_RET_FILE_EOF;
    } else if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "av_read_frame failed, ret:%d\n", ret);
        return HI_ERR_DEMUXER_READ_PACKET;
    }

    if (formatMember->pkt.size <= 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "HiffmpegDemux err: read stream len 0\n");
        return HI_ERR_DEMUXER_READ_PACKET;
    }

    return HI_SUCCESS;
}

static HI_S32 Mp4GetIFrame(FfDemuxFormatMember *formatMember, HI_S32 *firstStreamIndex, HI_U8 **buf, HI_U32 *dataLen)
{
    HI_BOOL getPacket = HI_FALSE;
    HI_S32 ret = HI_FAILURE;
    HI_U32 readTimes = 0;
    HI_S32 firstIndex = *firstStreamIndex;

    while (getPacket != HI_TRUE) {
        ret = Mp4GetFrame(formatMember);
        if (ret != HI_SUCCESS) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                "Mp4GetIFrame read frame failed ret=0x%x\n", ret);
            break;
        }
        if (firstIndex == HI_DEMUXER_NO_MEDIA_STREAM) {
            firstIndex = formatMember->pkt.stream_index;
            *firstStreamIndex = firstIndex;
        }

        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "stream_index=%d \n",
            formatMember->pkt.stream_index);

        if (formatMember->pkt.stream_index == formatMember->metaStreamIndex) {
            *buf = formatMember->pkt.data;
            *dataLen = formatMember->pkt.size;
            break;
        }
        av_packet_unref(&formatMember->pkt);

        readTimes++;
        if (readTimes > MAX_TRY_READ_TIMES) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "read frame for meta timeout \n");
            ret = HI_FAILURE;
            break;
        }
    }
    return ret;
}

static HI_S32 Mp4ParseSeekStreamStart(AVFormatContext *context, HI_S32 streamIndex)
{
    HI_S64 seekMin = INT64_MIN;
    HI_S64 seekMax = 0;
    HI_S32 seekFlag = 0;

    HI_S32 ret = avformat_seek_file(context, streamIndex, seekMin, 0, seekMax, seekFlag);
    if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_DEBUG,
            "error while seeking 0  metastream  ret:%d\n", ret);
        ret = HI_ERR_DEMUXER_SEEK;
    }
    return ret;
}

static HI_S32 Mp4ParseMetaIFrame(FfDemuxFormatMember *formatMember)
{
    HI_U8 *buf = HI_NULL;
    HI_U32 dataLen = 0;
    HI_U8 naluType = 0;
    HI_S32 firstStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;

    HI_S32 ret = Mp4GetIFrame(formatMember, &firstStreamIndex, &buf, &dataLen);
    if (ret != HI_SUCCESS) {
        goto PARSE_MEATAI_2STREAMSTART_END;
    }

    HI_U32 nalLen = Mp4GetOneNalUnit(&naluType, buf, dataLen);
    if (nalLen <= 4) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
            "Mp4GetOneNalUnit nalu len error %d \n", nalLen);
        goto PARSE_MEATAI_UNREF_END;
    }

    if (FFMPEG_AVC_SPS == (naluType & FFMPEG_H264_NAL_MASK)) {
        formatMember->metaStream->codecpar->codec_id = AV_CODEC_ID_H264;
    } else if (((naluType >> 1) & FFMPEG_H265_NAL_MASK) == FFMPEG_HEVC_SPS) {
        formatMember->metaStream->codecpar->codec_id = AV_CODEC_ID_HEVC;
    } else {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "I Frame codec type illegal:%d\n", naluType);
        goto PARSE_MEATAI_UNREF_END;
    }

    ret = Mp4GetMetaResolution(formatMember, buf + 4, nalLen - 4);
    if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "Mp4GetMetaResolution  failed \n");
        goto PARSE_MEATAI_UNREF_END;
    }

PARSE_MEATAI_UNREF_END:
    av_packet_unref(&formatMember->pkt);
PARSE_MEATAI_2STREAMSTART_END:
    if (firstStreamIndex != HI_DEMUXER_NO_MEDIA_STREAM) {
        if (Mp4ParseSeekStreamStart(formatMember->formatContext, firstStreamIndex) == HI_ERR_DEMUXER_SEEK) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "Mp4ParseSeekStreamStart failed \n");
        }
    }
    // pkt.pts is time base on stream->timebase, need expressed in AV_TIME_BASE units
    formatMember->lastReadPts = 0;
    return ret;
}

HI_S32 Mp4AvbsfInit(AVBSFContext **outBsfCtx, const AVStream *stream, const HI_CHAR *bsfName)
{
    AVBSFContext *bsfCtx = HI_NULL;

    const AVBitStreamFilter *absFilter = av_bsf_get_by_name(bsfName);
    if (!absFilter) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                       "Unknown bitstream filter %s\n", bsfName);
        return HI_FAILURE;
    }

    HI_S32 ret = av_bsf_alloc(absFilter, &bsfCtx);
    if ((ret < 0) || (bsfCtx == HI_NULL)) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                       "av_bsf_alloc failed ret %d\n", ret);
        return HI_FAILURE;
    }

    ret = avcodec_parameters_copy(bsfCtx->par_in, stream->codecpar);
    if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                       "avcodec_parameters_from_context failed ret %d\n", ret);
        av_bsf_free(&bsfCtx);
        return HI_FAILURE;
    }

    bsfCtx->time_base_in = stream->time_base;

    ret = av_bsf_init(bsfCtx);
    if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "Error initializing bitstream filter: %s\n",
                       bsfCtx->filter->name);
        av_bsf_free(&bsfCtx);
        return HI_FAILURE;
    }

    *outBsfCtx = bsfCtx;
    return HI_SUCCESS;
}

HI_VOID Mp4AvbsfDeinit(AVBSFContext **bsfCtx)
{
    if (bsfCtx != HI_NULL) {
        av_bsf_free(bsfCtx);
    }
}

static HI_S32 Mp4SetAudioPacketInfo(FfDemuxFormatMember *formatMember, HI_FORMAT_PACKET_S *fmtFrame,
    HI_U8 *adtsHeader, HI_U32 headLen)
{
    if (headLen < FFMPEG_AAC_ADTS_LEN) {
        return HI_FAILURE;
    }
    HI_BOOL hasADTS = Mp4HasAdtsHeader(adtsHeader, headLen,
        formatMember->pkt.data, formatMember->pkt.size);
    if (!hasADTS) {
        if (formatMember->pkt.size + headLen > FFMPEG_FRAME_BUF_DATA_LEN) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "audio data len beyond %d \n",
                           FFMPEG_FRAME_BUF_DATA_LEN);
            return HI_FAILURE;
        }

        if (memset_s(formatMember->audioData, FFMPEG_FRAME_BUF_DATA_LEN, 0, FFMPEG_FRAME_BUF_DATA_LEN) != EOK) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "set audio data fail\n");
            return HI_FAILURE;
        }
        if (memcpy_s(formatMember->audioData, headLen, adtsHeader, headLen) != EOK) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "copy audio adts fail\n");
            return HI_FAILURE;
        }
        if (memcpy_s(formatMember->audioData + headLen, formatMember->pkt.size,
            formatMember->pkt.data, formatMember->pkt.size) != EOK) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "set audio data fail\n");
            return HI_FAILURE;
        }

        fmtFrame->pu8Data = formatMember->audioData;
        fmtFrame->u32Size = formatMember->pkt.size + headLen;
    } else {
        fmtFrame->pu8Data = formatMember->pkt.data;
        fmtFrame->u32Size = formatMember->pkt.size;
    }

    formatMember->hasAdts = hasADTS;
    return HI_SUCCESS;
}

HI_S32 Mp4SetAudioInfo(FfDemuxFormatMember *formatMember, HI_FORMAT_PACKET_S *fmtFrame)
{
    // get  adts header for aac aduio
    HI_U32 sampleRateIdx = 3; /* 3 means 48000 sample rate, we use it as the default */
    HI_U32 channel = formatMember->audioStream->codecpar->channels;

    HI_U32 index;
    for (index = 0; index < AAC_SAMPLE_RATE_NUM; index++) {
        if ((HI_U32)formatMember->audioStream->codecpar->sample_rate == g_sampleRateTag[index]) {
            sampleRateIdx = index;
            break;
        }
    }

    if (index == AAC_SAMPLE_RATE_NUM) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
            "warning invalid sampleRate:%d,i guess as 48000HZ\n", formatMember->audioStream->codecpar->sample_rate);
    }

    /*
     * 0:Main profile
     * 1:Low Complexity profile(LC)
     * 2:Scalable Sampling Rate profile(SSR)
     * 3:(Reserved)
     */
    HI_U32 profile;
    if (formatMember->audioStream->codecpar->profile < 0 ||
        formatMember->audioStream->codecpar->profile >= 3) {
        profile = AAC_PROFILE_LC;
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO,
                       " warning invalid profile:%d,i guess Low Complexity profile\n",
                       formatMember->audioStream->codecpar->profile);
    } else {
        profile = formatMember->audioStream->codecpar->profile;
    }

    HI_U8 adtsHeader[FFMPEG_AAC_ADTS_LEN] = { 0 };
    FfAdtsHeaderPartInfo info;
    info.sampleRateidx = sampleRateIdx;
    info.frameLength = (HI_U32)(formatMember->pkt.size);
    info.profile = profile;
    info.chConfig = channel;
    Mp4GetAdtsHeader(info, adtsHeader, FFMPEG_AAC_ADTS_LEN);
    if (Mp4SetAudioPacketInfo(formatMember, fmtFrame, adtsHeader, FFMPEG_AAC_ADTS_LEN) != HI_SUCCESS) {
        return HI_FAILURE;
    }
    formatMember->lastReadPts = av_rescale_q(formatMember->pkt.pts - formatMember->audioStream->start_time,
        formatMember->audioStream->time_base, AV_TIME_BASE_Q);
    return HI_SUCCESS;
}

HI_S32 Mp4SetVideoInfo(FfDemuxFormatMember *formatMember, HI_FORMAT_PACKET_S *fmtFrame)
{
    if (((formatMember->vidCodecId == AV_CODEC_ID_H264) ||
        (formatMember->vidCodecId == AV_CODEC_ID_HEVC)) && !formatMember->playMetaVid) {
        HI_S32 ret = Mp4FilterPacket(formatMember);
        if (ret != HI_SUCCESS && ret != HI_RET_FILE_EOF) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "exec Mp4FilterPacket failed\n");
            return HI_ERR_DEMUXER_SKIP_FILE;
        } else if (ret != HI_SUCCESS) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "exec Mp4FilterPacket failed\n");
            return ret;
        }
    }

    // pkt.pts is time base on stream->timebase, need expressed in AV_TIME_BASE units
    formatMember->lastReadPts = av_rescale_q(formatMember->pkt.pts -
        formatMember->usedVideoStream->start_time, formatMember->usedVideoStream->time_base, AV_TIME_BASE_Q);
    fmtFrame->pu8Data = formatMember->pkt.data;
    fmtFrame->u32Size = formatMember->pkt.size;
    return HI_SUCCESS;
}

HI_S32 Mp4ProbeMeta(FfDemuxFormatMember *formatMember)
{
    HI_S32 ret = av_find_best_stream(formatMember->formatContext, AVMEDIA_TYPE_DATA, -1, -1, HI_NULL, 0);
    if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO,
            "Could not find metadata stream in input file '%s', ret:%d\n", formatMember->filePath, ret);
        formatMember->metaStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;
        formatMember->metaStream = HI_NULL;
    } else {
        formatMember->metaStream = formatMember->formatContext->streams[ret];
        formatMember->metaStreamIndex = ret;

        // get metadata width height codectype by parse first i frame
        if (Mp4ParseMetaIFrame(formatMember) != HI_SUCCESS) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "Mp4ParseMetaIFrame exec failed \n");
            formatMember->metaStream = HI_NULL;
            formatMember->metaStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;
        } else {
            formatMember->enMetaCodecId = formatMember->metaStream->codecpar->codec_id;
            if (formatMember->usedVideoStream == HI_NULL) {
                formatMember->usedVideoStreamIndex = formatMember->metaStreamIndex;
                formatMember->usedVideoStream = formatMember->metaStream;
                formatMember->enUsedVidCodecId = formatMember->enMetaCodecId;
                formatMember->playMetaVid = HI_TRUE;
            }

            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "meta codec type  %s\n",
                avcodec_get_name(formatMember->enMetaCodecId));
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "meta timebase: %d %d \n",
                formatMember->metaStream->time_base.den, formatMember->metaStream->time_base.num);
        }
    }
    return HI_SUCCESS;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
